package netty.echo;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Instruction：
 * Author：@author MaLi
 */
public class EchoServer {
    public void startServer() {
        //1, 创建启动器
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        //2, 创建并设置事件轮询组
        // 用于接收新连接
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        // 用于客户端与服务端的IO传输, 线程数默认为机器CPU核数*2
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        // 为父子通道设置对应的轮询组 - 本质是为通道设置Reactor的选择器角色
        serverBootstrap.group(bossGroup, workerGroup);

        //3, 设置Channel通信类型 - 这里使用NIO通信类型
        serverBootstrap.channel(NioServerSocketChannel.class);
        // 设置该通信为长链接
        serverBootstrap.option(ChannelOption.SO_KEEPALIVE, true);

        //4, 为子通道设置Handler流水线
        EchoServerHandler echoServerHandler = new EchoServerHandler();
        serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(echoServerHandler);
            }
        });

        //5, 启动并绑定Channel到服务器端口对应端口(这里是8888), sync()作用, 等待绑定完成
        try {
            ChannelFuture sync = serverBootstrap.bind(8888).sync();
            //6, 等待通道的关闭回调, (Netty官方案例有英文注释, 这里只是优雅的关闭方式, 实际在当前代码中并没有关闭事件)
            // 作用: 阻塞在这里一直等待新连接事件发生
            sync.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            //7, 关闭事件轮询组
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    //服务端处理IO事件的Handler处理器 - 基本Netty的开发都是开发Handler
    @ChannelHandler.Sharable //代表多个Channel可以共享这个Handler(只要没有并发安全就可以被共享)
    public static class EchoServerHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            //收到数据
            ByteBuf in = (ByteBuf) msg;
            //如果返回false则代表不是堆内分配的存储 - Netty4.1+使用直接内存(OS管理的物理内存)
            System.out.println("是否堆内存" + in.hasArray());
            //因为Netty4.1+默认是使用直接内存的buffer来存储Channel读到的数据, Java要进行处理这些数据, 先要拷贝到自己的堆中
            //所以这里先建立一个对应长度的堆内数组
            byte[] arr = new byte[in.readableBytes()];
            // 读取数据到数组arr中
            in.getBytes(0, arr);
            System.out.println("Data from Client: " + new String(arr));
            System.out.println("写回前，msg.refCnt:" + ((ByteBuf) msg).refCnt()); //测试一下当前缓冲区的引用数量
            //写数据回给客户端
            ChannelFuture channelFuture = ctx.writeAndFlush(msg);

            channelFuture.addListener((ChannelFuture futureListener) -> {
                System.out.println("写回后，msg.refCnt:" + ((ByteBuf) msg).refCnt());
            });
            //传递到下一个Handler - 当前案例只有一个用户自定义的Handler, 其实不写也无所谓.
            //super.channelRead(ctx, msg);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
        }
    }

    public static void main(String[] args) {
        EchoServer echoServer = new EchoServer();
        echoServer.startServer();
    }
}
